﻿// <copyright file="MapFrom.cs" company="DataAbstractor">
//   (c) Luke Geraghty 2013
// </copyright>
// <summary>
//   Destination class mapper.
// </summary>

namespace DataAbstractor
{
    using System;
    using System.Linq.Expressions;

    /// <summary>
    /// Destination class mapper.
    /// </summary>
    /// <typeparam name="TDestination">The destination type.</typeparam>
    /// <typeparam name="TSource">The source type.</typeparam>
    internal class MapFrom<TDestination, TSource> : IMapFrom<TDestination, TSource>
    {
        /// <summary>
        /// The mapping.
        /// </summary>
        private readonly MapEntry mapEntry;

        /// <summary>
        /// Initializes a new instance of the <see cref="MapFrom{TDestination,TSource}"/> class.
        /// </summary>
        /// <param name="mapEntry">The map entry.</param>
        public MapFrom(MapEntry mapEntry)
        {
            this.mapEntry = mapEntry;
        }

        /// <summary>
        /// Get the destination member to map.
        /// </summary>
        /// <param name="destination">The destination field.</param>
        /// <returns>Class for the source mapping.</returns>
        public IMapTo<TDestination, TSource> From(Expression<Func<TDestination, object>> destination)
        {
            if (destination == null)
            {
                throw new ArgumentNullException("destination");
            }

            var name = ExpressionHelper.GetPropertyName(destination.Body);
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentException("Unable to get member name from destination");
            }

            return new MapTo<TDestination, TSource>(this.mapEntry, name);
        }

        /// <summary>
        /// Ignore a destination member.
        /// </summary>
        /// <param name="destination">The destination field.</param>
        /// <returns>The current class.</returns>
        public IMapFrom<TDestination, TSource> Ignore(Expression<Func<TDestination, object>> destination)
        {
            if (destination == null)
            {
                throw new ArgumentNullException("destination");
            }

            var name = ExpressionHelper.GetPropertyName(destination.Body);
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentException("Unable to get member name from destination");
            }

            this.mapEntry.SetMap(name, new MapEntry.MapValue { Ignore = true });
            return this;
        }

        /// <summary>
        /// Run a method on mapping completion.
        /// </summary>
        /// <param name="method">Method to run.</param>
        /// <returns>The current class.</returns>
        public IMapFrom<TDestination, TSource> Run(Action<TDestination, TSource> method)
        {
            if (method == null)
            {
                throw new ArgumentNullException("method");
            }

            if (this.mapEntry.AbstractionComplete != null)
            {
                throw new MapException("Method is already set when abstraction is complete");
            }

            this.mapEntry.AbstractionComplete = (destination, source) => method((TDestination)destination, (TSource)source);
            return this;
        }
    }
}
